package pers.mihao.quickstream.publish.manager.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionTemplate;
import pers.mihao.quickstream.common.plus.lock.annotation.EnableGlobalLock;
import pers.mihao.quickstream.common.plus.lock.annotation.LockKey;
import pers.mihao.quickstream.common.task.domain.scheduler.AutomaticTaskTimer;
import pers.mihao.quickstream.common.task.domain.scheduler.InvokeFailRetryTaskTimer;
import pers.mihao.quickstream.common.task.dto.JavaAutomaticTaskDTO;
import pers.mihao.quickstream.common.task.dto.UniqueBizInfo;
import pers.mihao.quickstream.common.task.service.AutomaticTaskService;
import pers.mihao.quickstream.common.util.CollectionUtil;
import pers.mihao.quickstream.publish.constants.PublishTaskType;
import pers.mihao.quickstream.publish.constants.TaskConstants;
import pers.mihao.quickstream.publish.constants.TaskStatus;
import pers.mihao.quickstream.publish.repository.PublishDetailTaskRepository;
import pers.mihao.quickstream.publish.strategy.AccountChooseStrategy;
import pers.mihao.quickstream.publish.lister.PublishJobParams;
import pers.mihao.quickstream.publish.lister.PublishMaterialTaskInvoker;
import pers.mihao.quickstream.publish.manager.PublishTaskManager;
import pers.mihao.quickstream.publish.model.dto.*;
import pers.mihao.quickstream.publish.repository.PublishTaskRepository;
import pers.mihao.quickstream.publish.strategy.context.ChooseContext;
import pers.mihao.quickstream.publish.strategy.dto.ChooseAccount;
import pers.mihao.quickstream.publish.strategy.dto.ChooseAccountResult;

import javax.annotation.Resource;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static pers.mihao.quickstream.publish.constants.FeatureKey.MAIN_TASK_FAIL_REASON;
import static pers.mihao.quickstream.publish.constants.TaskStatus.INIT;

/**
 * @author mihao
 * @since 2024/11/22
 */
@Component
@Slf4j
public class PublishTaskManagerImpl implements PublishTaskManager {

    @Resource
    AutomaticTaskService automaticTaskService;

    @Resource
    PublishTaskRepository publishTaskRepository;

    @Resource
    PublishDetailTaskRepository publishDetailTaskRepository;

    @Resource
    AccountChooseStrategy accountChooseStrategy;

    @Resource
    TransactionTemplate transactionTemplate;

    @Override
    public void createPublicTask(PublishTaskReq publishTaskReq) {

        PublishTimeConfig publishTimeConfig = publishTaskReq.getPublishTimeConfig();
        PublishAccountConfig publishAccountConfig = publishTaskReq.getPublishAccountConfig();
        PublishExtInfo publishExtInfo = publishTaskReq.getPublishExtInfo();

        PublishTaskDTO publishTaskDTO = PublishTaskDTO.builder()
                .opUserId(publishTaskReq.getUserId())
                .materialId(publishTaskReq.getMaterialId())
                .taskStatus(INIT)
                .version(0)
                .timeTaskInfo(JSONObject.toJSONString(publishTimeConfig, SerializerFeature.WriteClassName))
                .publishAccountConfig(JSONObject.toJSONString(publishAccountConfig, SerializerFeature.WriteClassName))
                .publishExtInfo(JSONObject.toJSONString(publishExtInfo, SerializerFeature.WriteClassName))
                .build();
        publishTaskRepository.create(publishTaskDTO);
        // 立即执行，子任务定时执行 执行失败重试
        // todo 这里创建任务报错，或者重启会导致任务一直不会启动， 可以加定时任务对账
        sendTask(publishTaskDTO.getId(), PublishTaskType.MAIN_TASK, new InvokeFailRetryTaskTimer(System.currentTimeMillis(), 3));
    }

    private void sendTask(Long taskId, PublishTaskType publishTaskType, AutomaticTaskTimer taskTimer) {
        JavaAutomaticTaskDTO<PublishJobParams> javaTimeoutTaskDTO = new JavaAutomaticTaskDTO<>();
        javaTimeoutTaskDTO.setUniqueBizInfo(UniqueBizInfo.builder()
                .bizId(String.valueOf(taskId)).bizType(publishTaskType.getType()).build());
        javaTimeoutTaskDTO.setDoTaskInvoker(PublishMaterialTaskInvoker.class);
        PublishJobParams runJob = PublishJobParams.builder()
                .taskType(TaskConstants.MAIN_TASK)
                .taskId(taskId)
                .build();
        javaTimeoutTaskDTO.setJobParams(runJob);
        javaTimeoutTaskDTO.setRetryCount(2);
        // 立即执行，子任务定时执行 执行失败重试
        javaTimeoutTaskDTO.setAutomaticTaskTimer(taskTimer);
        automaticTaskService.createJavaAutomaticTask(javaTimeoutTaskDTO);
    }

    @Override
    @EnableGlobalLock
    public void parseDetailTask(@LockKey Long taskId) {
        PublishTaskDTO publishTaskDTO = publishTaskRepository.queryById(taskId);
        publishTaskDTO.setTaskBeginTime(new Date());
        TaskStatus taskStatus = publishTaskDTO.getTaskStatus();
        if (taskStatus != INIT) {
            log.error("parseDetailTask {} taskStatus is error {}", taskId, taskStatus);
            return;
        }

        ChooseContext chooseContext = initContext(publishTaskDTO);
        ChooseAccountResult chooseAccountResult = accountChooseStrategy.chooseAccount(chooseContext);
        List<ChooseAccount> accounts = chooseAccountResult.getAccounts();
        if (CollectionUtil.isEmpty(accounts)) {
            log.warn("publishMainTask {} parse Detail isFail no detail task", taskId);
            publishTaskDTO.setTaskStatus(TaskStatus.FAIL);
            publishTaskDTO.setTaskEndTime(new Date());
            publishTaskDTO.getFeatures().update(MAIN_TASK_FAIL_REASON, "为获取到子任务");
            publishTaskRepository.update(publishTaskDTO);
            return;
        }
        // 保存子任务
        List<PublishDetailTaskDTO> detailTaskDTOS = buildDetailTask(accounts, publishTaskDTO);

        transactionTemplate.execute(a -> {
            publishTaskDTO.setTaskEndTime(new Date());
            publishTaskDTO.setTaskStatus(TaskStatus.SUCCESS);
            publishTaskRepository.update(publishTaskDTO);
            for (PublishDetailTaskDTO detailTaskDTO : detailTaskDTOS) {
                publishDetailTaskRepository.create(detailTaskDTO);
            }
            return a;
        });
        // todo 这里创建子任务报错，或者重启会导致任务一直不会启动， 可以加定时任务对账
        String timeTaskInfo = publishTaskDTO.getTimeTaskInfo();
        AutomaticTaskTimer taskTimer = JSONObject.parseObject(timeTaskInfo, AutomaticTaskTimer.class);
        for (PublishDetailTaskDTO detailTaskDTO : detailTaskDTOS) {
            sendTask(detailTaskDTO.getId(), PublishTaskType.DETAIL_TASK, taskTimer);
        }
    }

    private List<PublishDetailTaskDTO> buildDetailTask(List<ChooseAccount> accounts, PublishTaskDTO publishTaskDTO) {
        return accounts.stream().map(a -> PublishDetailTaskDTO.builder()
                .parentTaskId(publishTaskDTO.getId())
                .invokeNum(0)
                .accountId(a.getAccountId())
                .platformCode(a.getPlatformCode())
                .version(0)
                .subTaskStatus(INIT)
                .build()).collect(Collectors.toList());
    }

    private ChooseContext initContext(PublishTaskDTO publishTaskDTO) {
        return new ChooseContext(publishTaskDTO);
    }
}
